package spinal.lib.bus.regif.Document

import spinal.core.GlobalData
import spinal.lib.bus.regif._
import scala.collection.mutable
import java.io.PrintWriter

final case class CHeaderGenerator(
    fileName : String,
    prefix : String,
    regType : String = "u32"
    ) extends BusIfVisitor {
        
    case class Reg(name : String, addr : Long)
    case class Field(name : String, width : Long, accessType : AccessType)
    case class Type(name : String, var fields : List[FieldDescr])

    val guardName : String = s"${prefix}_REGIF_H"
    val regs : mutable.ListBuffer[RegDescr] = mutable.ListBuffer[RegDescr]()
    val types : mutable.ListBuffer[Type] = mutable.ListBuffer[Type]()
    var regLength : Int = 0
    var addrLength : Int = 0
    
    def begin(busDataWidth : Int) : Unit = {

    }

    def visit(descr : FifoDescr)  : Unit = {

    }

    def visit(descr : RegDescr) : Unit = {
        def nameLen = descr.getName.length()
        def len = scala.math.log(descr.getAddr) / scala.math.log(16) + 1

        if(nameLen > regLength)
            regLength = nameLen

        if(len > addrLength)
            addrLength = len.toInt

        regs += descr
        types += Type(descr.getName, descr.getFieldDescrs)
    }
    
    def end() : Unit = {
        val pc = GlobalData.get.phaseContext
        val targetPath = s"${pc.config.targetDirectory}/${fileName}"
        val pw = new PrintWriter(targetPath)

        pw.write(
            s"""|/*
                | * Bus Interface Header - AUTOGENERATED
                | */
                |
                |#ifndef ${guardName}
                |#define ${guardName}
                |
                |""".stripMargin)
        
        for(reg <- regs) {
            pw.write(s"#define ${prefix.toUpperCase()}_${reg.getName.toUpperCase()} ")
            pw.write(" " * (regLength - reg.getName.length))
            pw.write("0x")
            pw.print(reg.getAddr.formatted(s"%0${addrLength}x"))
            pw.println()
        }
        pw.println()

        for(t <- types) {
            val naName = "reserved_"
            var i = -1
            val len = math.max(naName.length + 1, t.fields.map(_.getName.length()).fold(0)(math.max(_, _)))

            pw.println("typedef union {")

            pw.println(s"\t$regType val;")

            pw.println("\tstruct {")
            t.fields.foreach( f => {
                val name = f.getAccessType match {
                    case AccessType.NA => {
                        i += 1
                        s"reserved_${i}"
                    }
                    case default => f.getName
                }
                pw.write(s"\t\t$regType ${name} ")
                pw.write(" " * (len - name.length))                
                pw.println(s": ${f.getWidth.formatted("%2d")};")
            })

            pw.println("\t} reg;")
            pw.println(s"} ${prefix.toLowerCase}_${t.name.toLowerCase()}_t;\n")
        }
        
        pw.write(s"#endif /* ${guardName} */")

        pw.close()
    }
}